package com.woniuxy.book.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.ObjectUtil;
import com.alipay.api.internal.util.AlipaySignature;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.woniuxy.book.config.AlipayConfiguration;
import com.woniuxy.book.entity.BookInfo;
import com.woniuxy.book.entity.OrderInfo;
import com.woniuxy.book.entity.OrderItem;
import com.woniuxy.book.entity.PageInfo;
import com.woniuxy.book.exception.orderinfo.OrderInfoException;
import com.woniuxy.book.exception.orderinfo.OrderInfoExceptionCode;
import com.woniuxy.book.mapper.BookInfoMapper;
import com.woniuxy.book.mapper.OrderInfoMapper;
import com.woniuxy.book.mapper.OrderItemMapper;
import com.woniuxy.book.param.orderinfo.OrderParam;
import com.woniuxy.book.param.orderinfo.PagePayParam;
import com.woniuxy.book.service.OrderInfoService;
import com.woniuxy.book.service.dto.OrderInfoDTO;
import com.woniuxy.book.util.AlipayUtil;
import com.woniuxy.book.util.RedisKeyUtil;
import com.woniuxy.book.util.RedisUtil;
import com.woniuxy.book.util.StateData;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author li
 * @since 2024年2月09日
 */
@Service
public class OrderInfoServiceImpl implements OrderInfoService {

    @Resource
    private BookInfoMapper bookInfoMapper;
    @Resource
    private OrderInfoMapper orderInfoMapper;
    @Resource
    private OrderItemMapper orderItemMapper;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private AlipayUtil alipayUtil;

    @Resource
    private AlipayConfiguration alipayConfiguration;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Override
    @Transactional
    public OrderInfoDTO order(OrderParam param) throws Exception {
        //查询购物车信息
        List<Integer> list = param.getBookIdList();
        Collections.sort(list);//按照书籍ID排序保证后续的多资源锁顺序一致
        for (Integer bookId : list) {
            if (redisUtil.zscore(RedisKeyUtil.userShoppingCar(param.getUserId()), bookId + "") == null) {
                throw new Exception("购物车中没有该商品信息");
            }
        }
        //查询购物车详细信息
        ArrayList<Map<Object, Object>> shoppingOrderList = new ArrayList<>();
        //将多资源锁存到数组中
        RLock[] lockList = new RLock[list.size()];
        for (int i = 0; i < list.size(); i++) {
            Map<Object, Object> hashMap = redisUtil.hgetall(RedisKeyUtil.shoppingCarInfo(param.getUserId(), list.get(i)));
            //查询排序值
            hashMap.put("score", redisUtil.zscore(RedisKeyUtil.userShoppingCar(param.getUserId()), list.get(i) + ""));
            shoppingOrderList.add(hashMap);
            RLock rLock = redissonClient.getFairLock(RedisKeyUtil.lockBookId(list.get(i)));
            lockList[i] = rLock;
        }
        //合并为多资源锁
        RLock lock = redissonClient.getMultiLock(lockList);
        try {
            if (!lock.tryLock(10, TimeUnit.SECONDS)) {//加锁
                throw new Exception("服务器繁忙请稍后再试");
            }
            double total = 0;
            //查询库存量
            for (Map<Object, Object> item : shoppingOrderList) {
                Integer bookId = Integer.parseInt(item.get("bookId").toString());
                Integer number = Integer.parseInt(item.get("number").toString());
                BookInfo bookInfo = bookInfoMapper.selectById(bookId);
                if (bookInfo == null) {
                    throw new Exception("书籍信息不存在");
                }
                if (bookInfo.getBookState() != StateData.BOOK_STATE_NORMAL) {
                    throw new Exception("书籍已下架或删除");
                }
                if (bookInfo.getNumber() < number) {
                    throw new Exception(bookInfo.getBookShowname() + "库存不足");
                }
                //计算单种商品总金额
                Double price = Double.parseDouble(bookInfo.getBookPrice().multiply(new BigDecimal(number)).toString());
                //计算总金额
                total += price;
            }
            //使用雪花算法生成订单编号
            String orderNum = new Snowflake(1, 1).nextIdStr();
            OrderInfo orderInfo = BeanUtil.toBean(param, OrderInfo.class);
            orderInfo.setOrderNum(orderNum);
            orderInfo.setCreateTime(LocalDateTime.now());
            orderInfo.setOrderPrice(new BigDecimal(total));
            orderInfo.setOrderState(StateData.ORDER_STATE_NOPAY);
            orderInfoMapper.insert(orderInfo);
            try {
                //循环新增订单项以及冻结库存
                for (Map<Object, Object> item : shoppingOrderList) {
                    Integer bookId = Integer.parseInt(item.get("bookId").toString());
                    Integer number = Integer.parseInt(item.get("number").toString());
                    BookInfo bookInfo = bookInfoMapper.selectById(bookId);
                    //zset中删除BookId
                    redisUtil.zrem(RedisKeyUtil.userShoppingCar(param.getUserId()), bookId + "");
                    //删除HASH
                    redisUtil.delete(RedisKeyUtil.shoppingCarInfo(param.getUserId(), bookId));
                    OrderItem orderItem = new OrderItem();
                    orderItem.setOrderId(orderNum);
                    orderItem.setBookId(bookId);
                    orderItem.setBookCont(number);
                    orderItem.setBookImage(bookInfo.getBookImage());
                    orderItem.setBookName(bookInfo.getBookShowname());
                    orderItem.setBookPrice(bookInfo.getBookPrice());
                    orderItemMapper.insert(orderItem);
                    //冻结库存
                    bookInfo.setFreeze(bookInfo.getFreeze() + number);
                    bookInfo.setNumber(bookInfo.getNumber() - number);
                    bookInfoMapper.updateById(bookInfo);
                }
            } catch (Exception e) {
                for (Map<Object, Object> item : shoppingOrderList) {
                    String score = item.get("score").toString();
                    redisUtil.zadd(RedisKeyUtil.userShoppingCar(param.getUserId()),
                            item.get("bookId").toString(),
                            Double.parseDouble(score));
                    item.remove("score");
                    redisUtil.hmset(RedisKeyUtil.shoppingCarInfo(param.getUserId(),
                            Integer.parseInt(item.get("bookId").toString())), item);
                }
                throw e;
            }
            rabbitTemplate.convertAndSend("order-delay",orderNum);
            OrderInfoDTO dto = BeanUtil.toBean(orderInfo, OrderInfoDTO.class);
            return dto;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public String pagePay(PagePayParam param) throws Exception {
        QueryWrapper<OrderInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("order_num", param.getOrderId());
        wrapper.eq("user_id", param.getUserId());
        System.out.println(param);
        OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
        if (orderInfo == null) {
            throw new Exception("该订单不存在");
        }
        if (orderInfo.getOrderState() != StateData.ORDER_STATE_NOPAY) {
            throw new Exception("该订单已支付或已取消");
        }
        //设置参数
        param.setMoney(orderInfo.getOrderPrice().doubleValue());
        param.setSubject("IKUN图书网");
        param.setNotifyUrl("http://7j7xcu.natappfree.cc/orderInfo/notify");//异步路径
        param.setBody("图书订单支付");
        String form = alipayUtil.pagePay(param);
        return form;
    }

    @Override
    public PageInfo selectOrders(Integer pageNum, Integer pageSize, Integer orderState, Integer userId,String role) {
        QueryWrapper<OrderInfo> wrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotEmpty(orderState)) {
            wrapper.eq("order_state", orderState);
        }
        if (role.equals("用户")) {
            wrapper.eq("user_id", userId);
            wrapper.notIn("order_state",StateData.ORDER_STATE_DELETE);
        }
        Page<OrderInfo> page = new Page<OrderInfo>(pageNum == null ? 1 : pageNum, pageSize == null ? 5 : pageSize);
        orderInfoMapper.selectPage(page, wrapper);
        PageInfo pageInfo = BeanUtil.toBean(page, PageInfo.class);
        pageInfo.setPages(page.getPages());
        pageInfo.setList(page.getRecords());
        return pageInfo;
    }

    @Override
    @Transactional
    public void handlerOrder(Map<String, String> params) throws Exception {
        //在异步回调业务层验签 防止伪造请求
        boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayConfiguration.getAlipayPublicKey(), "utf-8", "RSA2"); //调用SDK验证签名
        if (!signVerified) {
            throw new Exception("签名错误");
        }
        //验签通过
        //取出订单编号
        String orderId = params.get("out_trade_no");
        String tradeStatus = params.get("trade_status");
        String payDate = params.get("gmt_payment");
        System.out.println(params);
        if ("TRADE_SUCCESS".equals(tradeStatus)) {
            //判断当前的订单状态是否是未支付
            OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
            if (orderInfo.getOrderState()== StateData.ORDER_STATE_NOPAY) {
                //修改订单状态
                orderInfo.setOrderState(StateData.ORDER_STATE_PAYED);
                orderInfo.setPayTime(LocalDateTime.parse(payDate, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                orderInfoMapper.updateById(orderInfo);
                //查询订单详情
                QueryWrapper<OrderItem> wrapper= new QueryWrapper<>();
                wrapper.eq("order_id",orderId);
                //排序防止死锁
                wrapper.orderByAsc("book_id");
                List<OrderItem> items = orderItemMapper.selectList(wrapper);
                //创建多资源锁
                RLock[] lockList = new RLock[items.size()];
                for (int i = 0; i < items.size(); i++) {
                    RLock rLock = redissonClient.getFairLock(RedisKeyUtil.lockBookId(items.get(i).getBookId()));
                    lockList[i] = rLock;
                }
                RLock lock = redissonClient.getMultiLock(lockList);
                try {
                    if (!lock.tryLock(10, TimeUnit.SECONDS)) {
                        throw new Exception("服务器繁忙");
                    }
                    for (OrderItem item : items) {
                        BookInfo bookInfo = bookInfoMapper.selectById(item.getBookId());
                        bookInfo.setFreeze(bookInfo.getFreeze()-item.getBookCont());
                        bookInfoMapper.updateById(bookInfo);
                    }
                } finally {
                    lock.unlock();
                }
            }
        }

    }

    @Override
    public void deleteOrder(String orderNum, Integer userId) {
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_num",orderNum);
        queryWrapper.eq("user_id",userId);
        OrderInfo orderInfo = orderInfoMapper.selectOne(queryWrapper);
        if (orderInfo==null) {
            throw new OrderInfoException(OrderInfoExceptionCode.ORDER_INFO_EXCEPTION_ORDER_NOTEXITS);
        }
        if (!(orderInfo.getOrderState()== StateData.ORDER_STATE_CANCEL||orderInfo.getOrderState()==StateData.ORDER_STATE_PAYED)) {
            throw new OrderInfoException(OrderInfoExceptionCode.ORDER_INFO_EXCEPTION_ORDER_BANDELETE);
        }

        orderInfo.setOrderState(StateData.ORDER_STATE_DELETE);
        orderInfoMapper.updateById(orderInfo);
    }

    @Override
    public OrderInfo findSingleOrder(String orderNum,Integer userId) {
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_num",orderNum);
        queryWrapper.eq("user_id",userId);
        OrderInfo orderInfo = orderInfoMapper.selectOne(queryWrapper);
        if (orderInfo==null) {
            throw new OrderInfoException(OrderInfoExceptionCode.ORDER_INFO_EXCEPTION_ORDER_NOTEXITS);
        }
        return orderInfo;
    }

}

